package com.globefrog.framework.web.filter;


import com.globefrog.framework.log.ActionLog;
import com.globefrog.framework.log.ActionLogImpl;
import com.globefrog.framework.log.TraceLogger;
import com.globefrog.framework.web.wrapper.HttpRequestWrapper;
import java.io.IOException;
import java.util.Enumeration;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

/**
 * 请求过滤器.
 *
 * @author : caijinbang
 * @date : Create in 2021-01-15
 */
@Slf4j
@Component
public class RequestActionFilter implements Filter {

  /**
   * 强制打印日志.
   */
  private static final String PARAM_FORCE_FLUSH_TRACE_LOG = "_trace";
  /**
   * 原始资源路径.
   */
  private static final String REFERER = "referer";


  public RequestActionFilter() {

  }

  @Override
  public void init(FilterConfig filterConfig) {

  }

  @Override
  public void doFilter(ServletRequest request, ServletResponse response,
      FilterChain filterChain) throws IOException, ServletException {

    if (request instanceof HttpServletRequest && response instanceof HttpServletResponse) {
      this.filter((HttpServletRequest) request, (HttpServletResponse) response, filterChain);
    } else {
      filterChain.doFilter(request, response);
    }
  }

  private void filter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
      throws IOException, ServletException {
    request.setCharacterEncoding("UTF-8");
    TraceLogger traceLogger = TraceLogger.getInstance();
    ActionLogImpl actionLogger = ActionLogImpl.getInstance();

    try {
      // 初始化traceLog
      traceLogger.initialize();
      // 初始化actionLog
      actionLogger.initialize();
      log.debug("=== begin request processing ===");
      HttpRequestWrapper requestWrapper = new HttpRequestWrapper(request);
      // 打印请求
      logRequest(requestWrapper, request);
      chain.doFilter(requestWrapper, response);
    } finally {
      logResponse(response, request);
      log.debug("=== finish request processing ===");
      actionLogger.save();
      traceLogger.cleanup(this.flushTraceLog(request));
      MDC.clear();
    }
  }

  private void logResponse(HttpServletResponse response, HttpServletRequest request) {
    int status = response.getStatus();
    log.debug("[response-status] responseHttpStatus={}", status);
    this.logHeaders(response);
    ActionLog actionLog = ActionLogImpl.getInstance().currentActionLog();
    actionLog.setHttpStatusCode(status);
    if (!StringUtils.hasText(actionLog.getRequestUri())) {
      actionLog.setRequestUri(request.getRequestURI());
    }
  }

  private void logRequest(HttpRequestWrapper requestWrapper, HttpServletRequest originalRequest) {
    log.debug("[request-context] requestURL={} | serverPort={} | method={} | dispatcherType={}",
        originalRequest.getRequestURL(), originalRequest.getServerPort(),
        originalRequest.getMethod(), originalRequest.getDispatcherType()
    );
    log.debug("[remote-context]remoteAddress={}", originalRequest.getRemoteAddr());
    ActionLog actionLog = ActionLogImpl.getInstance().currentActionLog();
    logHeaders(originalRequest, actionLog);
    logParameters(originalRequest);
    if (requestWrapper.isPreLoadBody()) {
      log.debug("[request-body] body={}", requestWrapper.getOriginalBody());
    }
    actionLog.setHttpMethod(originalRequest.getMethod());
  }

  /**
   * 打印响应头.
   *
   * @param response 响应
   */
  private void logHeaders(HttpServletResponse response) {
    for (String name : response.getHeaderNames()) {
      log.debug("[response-header] {}={}", name, response.getHeader(name));
    }
  }

  private void logHeaders(HttpServletRequest request, ActionLog actionLog) {
    Enumeration headers = request.getHeaderNames();
    while (headers.hasMoreElements()) {
      String headerName = (String) headers.nextElement();
      String headerValue = request.getHeader(headerName);
      if ("referer".equalsIgnoreCase(headerName)) {
        actionLog.logContext(REFERER, headerValue);
      }
      log.debug("[request-header] {}={}", headerName, headerValue);
    }
  }

  private void logParameters(HttpServletRequest request) {
    Enumeration paramNames = request.getParameterNames();
    while (paramNames.hasMoreElements()) {
      String paramName = (String) paramNames.nextElement();
      log.debug("[request-param] {}={}", paramName, request.getParameter(paramName));
    }
  }

  private boolean flushTraceLog(ServletRequest request) {
    String traceParam = request.getParameter(PARAM_FORCE_FLUSH_TRACE_LOG);
    return "true".equals(traceParam);
  }

  @Override
  public void destroy() {

  }
}
